#include "ArithFactory.h"
#include <cmath>
#include <cassert>

ArithFactory* ArithFactory::arithFracory=NULL;

ArithFactory* ArithFactory::getInstance(){
	
	if(arithFracory==NULL){
		arithFracory=new ArithFactory();
	}
	return arithFracory;
}

MyWord* ArithFactory::computeArith(std::string arith,std::vector<MyWord*>* childs){

	if(arith=="SUM"||arith=="ADD")	return computeSum(childs);
	else if(arith=="SUB")			return computeSub(childs);
	else if(arith=="MUL")			return computeMul(childs);
	else if(arith=="DIV")			return computeDiv(childs);
	else if(arith=="NEGATIVE")		return computeNegative(childs);
	else if(arith=="SQRT")			return computeSqrt(childs);
	else if(arith=="CEIL")			return computeCeil(childs);
	else if(arith=="FLOOR")			return computeFloor(childs);
	else if(arith=="SIN")			return computeSin(childs);
	else if(arith=="COS")			return computeCos(childs);
	else if(arith=="TAN")			return computeTan(childs);
	else if(arith=="ASIN")			return computeAsin(childs);
	else if(arith=="ACOS")			return computeAcos(childs);
	else if(arith=="ATAN")			return computeAtan(childs);
	else if(arith=="LOG")			return computeLog(childs);
	else if(arith=="LOG10")			return computeLog10(childs);
	else if(arith=="POW")			return computePow(childs);
	else if(arith=="INT")			return computeToInt(childs);
	else if(arith=="EXP")			return computeExp(childs);
	else if(arith=="MOD")			return computeMod(childs);
	else if(arith=="ABS")			return computeAbs(childs);
	else if(arith=="RESULT")		return computeResult(childs);
	else assert(false);
	return NULL;
}

void ArithFactory::clearList( std::vector<MyWord*>* list ){

	for( auto itr = list->begin();itr!=list->end();itr++)
		delete *itr;
	delete list;
}

MyWord* ArithFactory::computeSum( std::vector<MyWord*>* childs ){
	
	assert(childs->size()>0);
	
	MyWord* result=new MyWord(*childs->at(0));

	for(int i=1;i<childs->size();i++){
		*result+=*childs->at(i);
	}
	this->clearList(childs);
	return result;
}

MyWord* ArithFactory::computeSub( std::vector<MyWord*>* childs ){

	assert(childs->size()==2);

	MyWord* result=new MyWord(*childs->at(0));

	for(int i=1;i<childs->size();i++){
		*result-=*childs->at(i);
	}
	this->clearList(childs);
	return result;
}

MyWord* ArithFactory::computeMul( std::vector<MyWord*>* childs ){
	
	assert(childs->size()==2);

	MyWord* result=new MyWord(*childs->at(0));

	for(int i=1;i<childs->size();i++){
		*result*=*childs->at(i);
	}
	this->clearList(childs);
	return result;
}

MyWord* ArithFactory::computeDiv( std::vector<MyWord*>* childs ){
	
	assert(childs->size()==2);

	MyWord* result=new MyWord(*childs->at(0));

	for(int i=1;i<childs->size();i++){
		*result/=*childs->at(i);
	}
	this->clearList(childs);
	return result;
}

MyWord* ArithFactory::computeNegative( std::vector<MyWord*>* childs ){
	
	assert(childs->size()==1);

	MyWord* result=new MyWord(*childs->at(0));

	if(result->_type=="int"){
		result->_value_int*=-1;
	}
	else if(result->_type=="FLOAT"){
		result->_value_float*=-1;
	}
	this->clearList(childs);
	return result;
}

MyWord* ArithFactory::computeSqrt( std::vector<MyWord*>* childs ){
	
	assert(childs->size()==1);

	MyWord* result=new MyWord(*childs->at(0));

	if(result->_type=="int"){
		result->_value_double=sqrt((double)result->_value_int);
	}
	else if(result->_type=="double"){
		result->_value_double=sqrt(result->_value_double);
	}
	result->_type="double";
	this->clearList(childs);
	return result;
}

MyWord* ArithFactory::computeCeil( std::vector<MyWord*>* childs ){
	
	assert(childs->size()==1);

	MyWord* result=new MyWord(*childs->at(0));

	if(result->_type=="int"){
		result->_value_double=ceil((double)result->_value_int);
	}
	else if(result->_type=="double"){
		result->_value_double=ceil(result->_value_double);
	}
	result->_type="double";
	this->clearList(childs);
	return result;
}

MyWord* ArithFactory::computeFloor( std::vector<MyWord*>* childs ){
	
	assert(childs->size()==1);

	MyWord* result=new MyWord(*childs->at(0));

	if(result->_type=="int"){
		result->_value_double=floor((double)result->_value_int);
	}
	else if(result->_type=="double"){
		result->_value_double=floor(result->_value_double);
	}
	result->_type="double";
	this->clearList(childs);
	return result;
}

MyWord* ArithFactory::computeSin( std::vector<MyWord*>* childs ){

	assert(childs->size()==1);

	MyWord* result=new MyWord(*childs->at(0));

	if(result->_type=="int"){
		result->_value_double=sin((double)result->_value_int);
	}
	else if(result->_type=="double"){
		result->_value_double=sin(result->_value_double);
	}
	result->_type="double";
	this->clearList(childs);
	return result;
}

MyWord* ArithFactory::computeCos( std::vector<MyWord*>* childs ){
	
	assert(childs->size()==1);

	MyWord* result=new MyWord(*childs->at(0));

	if(result->_type=="int"){
		result->_value_double=cos((double)result->_value_int);
	}
	else if(result->_type=="double"){
		result->_value_double=cos(result->_value_double);
	}
	result->_type="double";
	this->clearList(childs);
	return result;
}

MyWord* ArithFactory::computeTan( std::vector<MyWord*>* childs ){

	assert(childs->size()==1);

	MyWord* result=new MyWord(*childs->at(0));

	if(result->_type=="int"){
		result->_value_double=tan((double)result->_value_int);
	}
	else if(result->_type=="double"){
		result->_value_double=tan(result->_value_double);
	}
	result->_type="double";
	this->clearList(childs);
	return result;
}

MyWord* ArithFactory::computeAsin( std::vector<MyWord*>* childs ){

	assert(childs->size()==1);

	MyWord* result=new MyWord(*childs->at(0));

	if(result->_type=="int"){
		result->_value_double=asin((double)result->_value_int);
	}
	else if(result->_type=="double"){
		result->_value_double=asin(result->_value_double);
	}
	result->_type="double";
	this->clearList(childs);
	return result;
}

MyWord* ArithFactory::computeAcos( std::vector<MyWord*>* childs ){

	assert(childs->size()==1);

	MyWord* result=new MyWord(*childs->at(0));

	if(result->_type=="int"){
		result->_value_double=acos((double)result->_value_int);
	}
	else if(result->_type=="double"){
		result->_value_double=acos(result->_value_double);
	}
	result->_type="double";
	this->clearList(childs);
	return result;
}

MyWord* ArithFactory::computeAtan( std::vector<MyWord*>* childs ){

	assert(childs->size()==1);

	MyWord* result=new MyWord(*childs->at(0));

	if(result->_type=="int"){
		result->_value_double=atan((double)result->_value_int);
	}
	else if(result->_type=="double"){
		result->_value_double=atan(result->_value_double);
	}
	result->_type="double";
	this->clearList(childs);
	return result;
}

MyWord* ArithFactory::computeLog( std::vector<MyWord*>* childs ){

	assert(childs->size()==1);

	MyWord* result=new MyWord(*childs->at(0));

	if(result->_type=="int"){
		result->_value_double=log((double)result->_value_int);
	}
	else if(result->_type=="double"){
		result->_value_double=log(result->_value_double);
	}
	result->_type="double";
	this->clearList(childs);
	return result;
}

MyWord* ArithFactory::computeLog10( std::vector<MyWord*>* childs ){

	assert(childs->size()==1);

	MyWord* result=new MyWord(*childs->at(0));

	if(result->_type=="int"){
		result->_value_double=log10((double)result->_value_int);
	}
	else if(result->_type=="double"){
		result->_value_double=log10(result->_value_double);
	}
	result->_type="double";
	this->clearList(childs);
	return result;
}

MyWord* ArithFactory::computePow( std::vector<MyWord*>* childs ){	

	assert(childs->size()==2);

	MyWord* result=new MyWord(*childs->at(0));

	MyWord* expWord=new MyWord(*childs->at(1));
	double exp;
	if(expWord->_type=="int"){
		exp=(double)expWord->_value_int;
	}
	else if(expWord->_type=="double"){
		exp=expWord->_value_double;
	}

	if(result->_type=="int"){
		result->_value_double=pow((double)result->_value_int,exp);
	}
	else if(result->_type=="double"){
		result->_value_double=pow(result->_value_double,exp);
	}
	result->_type="double";
	this->clearList(childs);
	return result;
}

MyWord* ArithFactory::computeToInt( std::vector<MyWord*>* childs ){

	assert(childs->size()==1);

	MyWord* result=new MyWord(*childs->at(0));

	if(result->_type=="double"){
		result->_value_int=(int)result->_value_double;
	}
	result->_type="int";
	this->clearList(childs);
	return result;
}

MyWord* ArithFactory::computeExp( std::vector<MyWord*>* childs ){

	assert(childs->size()==1);

	MyWord* result=new MyWord(*childs->at(0));

	if(result->_type=="int"){
		result->_value_double=exp((double)result->_value_int);
	}
	else if(result->_type=="double"){
		result->_value_double=exp(result->_value_double);
	}
	result->_type="double";
	this->clearList(childs);
	return result;
}

MyWord* ArithFactory::computeMod( std::vector<MyWord*>* childs ){
	assert(childs->size()==2);

	MyWord* result=new MyWord(*childs->at(0));

	MyWord* expWord=new MyWord(*childs->at(1));
	double exp;
	if(expWord->_type=="int"){
		exp=(double)expWord->_value_int;
	}
	else if(expWord->_type=="double"){
		exp=expWord->_value_double;
	}

	if(result->_type=="int"){
		result->_value_double=fmod((double)result->_value_int,exp);
	}
	else if(result->_type=="double"){
		result->_value_double=fmod(result->_value_double,exp);
	}
	result->_type="double";
	this->clearList(childs);
	return result;
}

MyWord* ArithFactory::computeAbs( std::vector<MyWord*>* childs ){
	assert(childs->size()==1);

	MyWord* result=new MyWord(*childs->at(0));

	if(result->_type=="int"){
		result->_value_int=(int)fabs((double)result->_value_int);
	}
	else if(result->_type=="double"){
		result->_value_double=fabs(result->_value_double);
	}
	this->clearList(childs);
	return result;
}

MyWord* ArithFactory::computeResult( std::vector<MyWord*>* childs ){

	return NULL;
}

